import { Component, Fragment } from "react";
import {
  storeArrType,
  rBusProtoType,
  typeReact,
  ProxyType_proxStores,
  ProxyType_store,
  rstoreAnyType,
  BusArrType,
  StatusArrType,
  ProxyType_proxBus,
  ProxyType_proxStatus,
  ProxyType_bus,
  ProxyType_status,
} from "../types";
import { rStatus } from "../prototype";
export default (
    storeArr: storeArrType,
    busArr: BusArrType,
    StatusArr: StatusArrType
  ) =>
  (ProxyStore: Function, storeKayArr: Array<string>) => {
    const proxStores: ProxyType_proxStores = {},
      data_store: ProxyType_store["data"] = {},
      data_status: ProxyType_store["data"] = {},
      actions_store: ProxyType_store["actions"] = {},
      actions_bus: ProxyType_store["actions"] = {},
      actions_status: ProxyType_store["actions"] = {},
      proxBus: ProxyType_proxBus = {},
      proxStatus: ProxyType_proxStatus = {};
    storeArr.forEach((storeCompenent) => {
      storeKayArr.forEach((key) => {
        if (key === storeCompenent.storeName) {
          proxStores[key] = storeCompenent;
          data_store[key] = storeCompenent.data;
          actions_store[key] = storeCompenent.actions;
        }
      });
    });
    StatusArr.forEach((statusComponent) => {
      storeKayArr.forEach((key) => {
        if (key === statusComponent.storeName) {
          proxStatus[key] = statusComponent;
          data_status[key] = statusComponent.data;
          actions_status[key] = statusComponent.actions;
        }
      });
    });
    busArr.forEach((busComponent) => {
      storeKayArr.forEach((key) => {
        if (key === busComponent.storeName) {
          proxBus[key] = busComponent;
          if (busComponent.actions) {
            actions_bus[key] = busComponent.actions;
          }
        }
      });
    });
    const functionFactory = (
      funName: keyof rstoreAnyType,
      value: any,
      storeName: string,
      proxyComponents?: typeReact
    ) => {
      if (!proxStores[storeName] || !proxStores[storeName][funName]) return;
      proxStores[storeName][funName](value, proxyComponents);
    };
    const busFunctionFactory = (
      funName: keyof rstoreAnyType,
      storeName: string,
      value1?: any,
      value2?: any,
      value3?: any
    ) => {
      if (!proxStores[storeName] || !proxStores[storeName][funName]) return;
      if (value1 && value2) {
        proxStores[storeName][funName](value1, value2);
      } else if (value1 && value3) {
        proxStores[storeName][funName](value1, ...value3);
      } else if (value1) {
        proxStores[storeName][funName](value1);
      }
    };
    const functionFactory_proxStatus = (
      funName: keyof rStatus<any>,
      value: any,
      storeName: string,
      proxyComponents?: typeReact
    ) => {
      if (!proxStatus[storeName] || !proxStatus[storeName][funName]) return;
      proxStatus[storeName][funName](value, proxyComponents);
    };
    const busFunctionFactory_proxBus = (
      funName: keyof rBusProtoType,
      storeName: string,
      value1?: any,
      value2?: any,
      value3?: any
    ) => {
      if (!proxBus[storeName] || !proxBus[storeName][funName]) return;
      if (
        funName !== "function_that_requires_asynchronous_processing" &&
        funName !== "listState"
      ) {
        if (value1 && value2) {
          proxBus[storeName][funName](value1, value2);
        } else if (value1 && value3) {
          proxBus[storeName][funName](value1, value3);
        } else if (
          value1 &&
          (funName === "changeState" || funName === "asyncChangeState")
        ) {
          proxBus[storeName][funName](value1);
        }
      }
    };
    class ProxStore extends Component {
      store: ProxyType_store;
      bus: ProxyType_bus;
      status: ProxyType_status;
      constructor(props: { [name: string]: any }) {
        super(props);
        this.store = {
          bindData: (dataKeysArr: Array<string>, storeName: string) =>
            functionFactory("bindData", dataKeysArr, storeName, this),
          unBindData: (dataKeysArr: Array<string>, storeName: string) =>
            functionFactory("unBindData", dataKeysArr, storeName, this),
          responseCallback: (fun: Function, storeName: string) =>
            functionFactory("responseCallback", fun, storeName),
          changeData: (data: { [name: string]: any }, storeName: string) =>
            functionFactory("changeData", data, storeName),
          clearData: (dataKeys: Array<string>, storeName: string) =>
            functionFactory("clearData", dataKeys, storeName),
          pushState: (str: string, fun: Function, storeName: string) =>
            busFunctionFactory("pushState", storeName, str, fun),
          changeState: (
            name: string,
            valueArr: Array<any>,
            storeName: string
          ) =>
            busFunctionFactory(
              "changeState",
              storeName,
              name,
              undefined,
              valueArr
            ),
          clearMethods: (
            mothodsNameArr: Array<string>,
            isAsync: boolean,
            storeName: string
          ) =>
            busFunctionFactory(
              "clearMethods",
              storeName,
              mothodsNameArr,
              isAsync
            ),
          asyncPushState: (str: string, fun: Function, storeName: string) =>
            busFunctionFactory("asyncPushState", storeName, str, fun),
          asyncChangeState: async (
            name: string,
            valueArr: Array<any>,
            storeName: string
          ) => {
            if (
              !proxStores[storeName] ||
              !proxStores[storeName].asyncChangeState
            )
              return;
            return await proxStores[storeName].asyncChangeState!(
              name,
              valueArr
            );
          },
          data: data_store,
          actions: actions_store,
        };
        this.bus = {
          pushState: (str: string, fun: Function, storeName: string) =>
            busFunctionFactory_proxBus("pushState", storeName, str, fun),
          changeState: (
            name: string,
            valueArr: Array<any>,
            storeName: string
          ) =>
            busFunctionFactory_proxBus(
              "changeState",
              storeName,
              name,
              undefined,
              valueArr
            ),
          clearMethods: (
            mothodsNameArr: Array<string>,
            isAsync: boolean,
            storeName: string
          ) =>
            busFunctionFactory_proxBus(
              "clearMethods",
              storeName,
              mothodsNameArr,
              isAsync
            ),
          asyncPushState: (str: string, fun: Function, storeName: string) =>
            busFunctionFactory_proxBus("asyncPushState", storeName, str, fun),
          asyncChangeState: async (
            name: string,
            valueArr: Array<any>,
            storeName: string
          ) => {
            if (
              !proxStores[storeName] ||
              !proxStores[storeName].asyncChangeState
            )
              return;
            return await proxStores[storeName].asyncChangeState!(
              name,
              valueArr
            );
          },
          actions: actions_bus,
        };
        this.status = {
          bindData: (dataKeysArr: Array<string>, storeName: string) =>
            functionFactory_proxStatus(
              "bindData",
              dataKeysArr,
              storeName,
              this
            ),
          unBindData: (dataKeysArr: Array<string>, storeName: string) =>
            functionFactory_proxStatus(
              "unBindData",
              dataKeysArr,
              storeName,
              this
            ),
          responseCallback: (fun: Function, storeName: string) =>
            functionFactory_proxStatus("responseCallback", fun, storeName),
          changeData: (data: { [name: string]: any }, storeName: string) =>
            functionFactory_proxStatus("changeData", data, storeName),
          clearData: (dataKeys: Array<string>, storeName: string) =>
            functionFactory_proxStatus("clearData", dataKeys, storeName),
          data: data_status,
          actions: actions_status,
        };
      }
      render() {
        return (
          <Fragment>
            <ProxyStore
              {...this.props}
              rStore={this.store}
              rBus={this.bus}
              rStatus={this.status}
            />
          </Fragment>
        );
      }
    }
    return ProxStore;
  };
